//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#include "WSNNetworkStackTrafficGen.h"
#include "NetwToMacControlInfo.h"
#include <cassert>
#include <Packet.h>
#include <BaseMacLayer.h>


Define_Module(WSNNetworkStackTrafficGen);

void WSNNetworkStackTrafficGen::initialize(int stage)
{
	BaseLayer::initialize(stage);

	if(stage == 0) {
		world = FindModule<BaseWorldUtility*>::findGlobalModule();
		delayTimer = new cMessage("delay-timer", SEND_BROADCAST_TIMER);
		sensorDataTimer = new cMessage("sensorData", SEND_SENSOR_TIMER);

		arp = FindModule<BaseArp*>::findSubModule(findHost());
		myNetwAddr = arp->myNetwAddr(this);
		//myNetwAddr = getParentModule()->getId();

		packetLength = par("packetLength");
		packetTime = par("packetTime");
		pppt = par("packetsPerPacketTime");
		burstSize = par("burstSize");
		destination = par("destination");
		setupTime = par("setupTime");
		sendInterval = par("sendInterval");
		isSink = par("isSink");

		PacketCreatedCounter = 0 ;

		Packet p(1);
		catPacket = world->getCategory(&p);
	} else if (stage == 1) {

		if(burstSize > 0 && isSink) {
			remainingBurst = burstSize;
			scheduleAt(setupTime+dblrand() * packetTime * burstSize / pppt, delayTimer);
		}
	}
}

WSNNetworkStackTrafficGen::~WSNNetworkStackTrafficGen() {
	cancelAndDelete(delayTimer);
	cancelAndDelete(sensorDataTimer);
}


void WSNNetworkStackTrafficGen::finish()
{
	if(isSink){
		averageDelay.setName("average Delay");
		std::string averName = "AverageDelay";
		std::string it = "0";
		for(int i=0;i<PRIORITY_LNUM;++i) {
			averName = "AverageDelay";
			it = '0' +i;
			averName.append(it);
			averageDelay.recordWithTimestamp(i,delayStats[i].getMean());
			delayStats[i].recordAs(averName.c_str());
		}
	}else{
		nbPacketDropped.recordAs("droped package");
		nbPacketCreated.recordAs("created package");
	}
}

void WSNNetworkStackTrafficGen::handleSelfMsg(cMessage *msg)
{
	switch( msg->getKind() )
	{
	case SEND_BROADCAST_TIMER:
		assert(msg == delayTimer);
		sendBroadcast();
		scheduleAt(simTime()+sendInterval*10, delayTimer);
		break;
	case SEND_SENSOR_TIMER:
		sendSensorDate();
		scheduleAt(simTime()+sendInterval+dblrand()*100, sensorDataTimer);
		break;
	default:
		EV << "Unkown selfmessage! -> delete, kind: "<<msg->getKind() <<endl;
		delete msg;
	}
}


void WSNNetworkStackTrafficGen::handleLowerMsg(cMessage *msg)
{
	Sensor *sensorPkt = NULL;
	switch(msg->getKind())
	{
	case SENSOR_MESSAGE:
		sensorPkt = static_cast<Sensor*>(msg);
		assert(sensorPkt);
		delayStats[sensorPkt->getPriorty()].collect(simTime()-sensorPkt->getSendTime());
		break;
	case BROADCAST_MESSAGE:
		sensorPkt = static_cast<Sensor*>(msg);
		assert(sensorPkt);
		destination = sensorPkt->getSrcAddr();
		sendSensorDate();
		if(!sensorDataTimer->isScheduled())
			scheduleAt(simTime()+sendInterval+dblrand()*100, sensorDataTimer);
		break;
	}
	Packet p(packetLength, 1, 0);
	world->publishBBItem(catPacket, &p, -1);
	delete msg;
	msg = 0;
}


void WSNNetworkStackTrafficGen::handleLowerControl(cMessage *msg)
{
	if(msg->getKind() == BaseMacLayer::PACKET_DROPPED) {
		nbPacketDropped.collect(msg->getSchedulingPriority());
	}
	delete msg;
	msg = 0;
}

void WSNNetworkStackTrafficGen::sendBroadcast()
{
	int broadcastDes = -1;
	Sensor *pkt = new Sensor("BROADCAST_MESSAGE", BROADCAST_MESSAGE);
	pkt->setBitLength(packetLength);

	pkt->setSrcAddr(myNetwAddr);
	pkt->setDestAddr(broadcastDes);

	pkt->setSendTime(simTime());
	pkt->setControlInfo(new NetwToMacControlInfo(broadcastDes));

	Packet p(packetLength, 0, 1);
	world->publishBBItem(catPacket, &p, -1);
	PacketCreatedCounter++;
	sendDown(pkt);
}

void WSNNetworkStackTrafficGen::sendSensorDate()
{
	int macAdd = arp->getMacAddr(destination);
	Sensor *pkt = new Sensor("SensorDate", SENSOR_MESSAGE);
	pkt->setBitLength(packetLength);
	pkt->setSrcAddr(myNetwAddr);
	pkt->setDestAddr(destination);
	pkt->setSendTime(simTime());
	pkt->setControlInfo(new NetwToMacControlInfo(macAdd));
	//pkt->setPriorty(intrand(PRIORITY_LNUM));
	pkt->setPriorty(PacketCreatedCounter%PRIORITY_LNUM);
	Packet p(packetLength, 0, 1);
	world->publishBBItem(catPacket, &p, -1);
	PacketCreatedCounter++;
	nbPacketCreated.collect(PacketCreatedCounter%PRIORITY_LNUM);
	sendDown(pkt);
}

